Internet Engineering Task Force Robert E. Gilligan INTERNET-DRAFT Sun Microsystems, Inc. March 26, 1993 SIP Program Interfaces for BSD Systems Abstract In order to implement the Simple Internet Protocol (SIP) [1] in an operating system based on 4.x BSD, changes must be made to some of the application program interfaces (APIs). TCP/IP applications written for BSD-based operating systems have in the past enjoyed a high degree of portability because most of the systems derived from BSD provide the same API, known informally as "the socket interface". We would like the same portability to be possible with SIP applications. This memo presents a set of changes to the BSD socket API to support SIP. The changes include a new data structure to carry 64-bit SIP addresses, a new name to address translation library function, new address conversion functions, and some new setsockopt() options. The changes are designed to be minimal and to provide source and binary compatibility for existing applications. Status of this Memo Internet Drafts are working documents of the Internet Engineering Task Force (IETF), its Areas, and its Working Groups. Note that other groups may also distribute working documents as Internet Drafts. Internet Drafts are draft documents valid for a maximum of six months. This Internet Draft expires on September 26, 1993. Internet Drafts may be updated, replaced, or obsoleted by other documents at any time. It is not appropriate to use Internet Drafts as reference material or to cite them other than as a "working draft" or "work in progress." Please check the I-D abstract listing contained in each Internet Draft directory to learn the current status of this or any other Internet Draft. Distribution of this memo is unlimited. 1. Introduction. The most visible feature of SIP is its extension of the length of an IP address from 32 bits to 64 bits. The APIs that the BSD system provides make the size of an IP address quite visible to an application. Virtually all TCP/IP applications for BSD-based systems have knowledge of the size of an IP address. Generally speaking, those parts of the API that make addresses visible need to be changed. This paper presents a first attempt to define the API changes needed to support SIP in BSD systems. These APIs are expected to evolve as we gain more implementation experience. 2. Design considerations There are two important considerations in designing changes to these well-worn APIs. First, the modified API should provide both source and binary compatibility for programs written to the existing API. That is, exiting program binaries should continue to operate when run on or re-compiled and run a system supporting the new APIs. The API changes for SIP should not cause existing program binaries to break, nor should they cause programs written to the existing APIs to fail to compile or break when re-compiled. Second, the changes should be as minimal as possible and should make it as easy as possible to change an existing application to be SIP knowledgeable. 2.1 What needs to be changed Since their address arguments are all opaque pointers, none of the socket system calls need to changed directly. The in_addr structure is 4-bytes long and can not be changed without breaking applications. The sockaddr_in structure, which is passed into and returned by the socket functions, contains 8 bytes of unused space which can be used to hold a SIP address without incompatibility. The gethostbyaddr() function includes an address type argument, so this function does not need to be changed. However, the gethostbyname() function does not include a type argument, and most applications that use it assume that the address array that it returns consists of 4-byte IP addresses. Thus a new function similar to gethostbyname(), but knowledgeable of SIP addresses, must be defined. Finally, a new interface is needed in order to set the SIP flow ID. The most straightforward way to do this is to define new setsockopt() options at the IPPROTO_IP level. 2.2 Implementation experience The Sun IPAE/SIP prototype exposed some of the issues in designing a compatible interface. The initial prototype passed SIP addresses between the kernel and applications using the sockaddr_in structure with the sin_family field set to AF_INET. The 8-byte SIP address occupied the 4 bytes of the "sin_addr" field plus the first 4 bytes of the "sin_zero" field. The kernel distinguished between 8-byte SIP addresses and 4-byte IPv4 addresses passed in by checking the sin_zero field. If sin_zero was zero, the kernel would assume that the sin_addr field held a 4-byte IPv4 address. If sin_zero was non-zero, it would assume that an 8-byte SIP address was being passed in. While this technique works for most existing applications, we discovered that some applications do not set the sin_zero field of the sockaddr_in structure to zero. Thus we concluded that it would be unworkable in the general case and that some specific indication was needed to distinguish a 8-byte SIP address from a 4-byte IPv4 address in a sockaddr_in structure. Another useful lesson learned from the initial Sun implementation was that the form of addresses (IPv4 or SIP) passed in and out of the socket functions did not always need to constrain the form of packets (IPv4, IPAE or SIP) transmitted or received. For example, a pre-existing TCP server application that knows nothing of SIP could accept a connection that was transmitting and receiving TCP segments encapsulated within SIP packets. Because SIP and IPv4 addresses are mappable, the accept() system call can return to the application the client's address as a 4-byte IPv4 address. What does matter, however, is that pre-existing applications never be given address structures that they don't understand. We found that the most convenient way for the kernel to decide what form of address to pass back to applications was to look at the form of addresses passed in. That is, if an application passes in IPv4 form addresses, then it should always be given IPv4 form addresses. If the application passes in SIP form addresses, the kernel can return SIP form addresses to the application. 3. Interface specification 3.1. New address family We define a address family macro in : #define AF_SIP 24 /* Simple Internet Protocol */ The AF_SIP definition is used only to distinguish between the original AF_INET form of the sockaddr_in structure, and a new form that we have defined. Thus there is no corresponding PF_SIP definition. 3.2 Sockaddr_in structure The sockaddr_in structure is used in the socket system calls to pass addresses between the application and the kernel. Keeping the sockaddr_in structure the same size is necessary to provide binary compatibility for existing IPv4 applications. We can avoid changing the size of the sockaddr_in structure by appropriating the currently unused sin_zero field to hold the 64 bit SIP address. We can add a structure element for the SIP address without changing the original structure definition by just adding two new macros. The resulting definition in the header file is: struct sockaddr_in { short sin_family; u_short sin_port; in_addr sin_addr; char sin_zero[8]; } #define sip_addr sin_zero #define sip_zero sin_addr The value of the sin_family field determines whether the sockaddr_in structure is holding a 32 bit IPv4 address or a 64 bit SIP address. The sin_family field may have one of two values: AF_INET or AF_SIP. If sin_family is AF_SIP, then the sip_addr field contains a 8-byte SIP address and the sip_zero field must be set to zero. If the sin_family field is AF_INET, then the sockaddr_in keeps its original semantics: The sin_addr field contains a 4-byte IPv4 address and the sin_zero field must be set to zero. We refer to the the two variants as the "AF_INET form" or the "AF_SIP form" of the sockaddr_in structure. The two structure layouts can be understood better when viewed graphically. This is the AF_INET form of the sockaddr_in structure: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | AF_INET | port number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | IPv4 address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + all zero bits + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ And this is the layout of the AF_SIP form of the sockaddr_in structure: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | AF_SIP | port number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | all zero bits | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + SIP address + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ We considered an alternative form that overlays the high-order four bytes of the SIP address with sin_addr. The form presented has a few advantages: - The SIP address is aligned on a 64-bit boundary within the structure. - The kernel can do some consistency checking on AF_SIP addresses passed in. Because the sip_zero field, which overlays the sin_addr field, must always zero, the kernel can detect cases where the application incorrectly treats an AF_SIP form sockaddr_in as an AF_INET form. - The structure declaration is simpler than the alternative. 3.3 The socket functions Even though we define a new address family for SIP, there is no change to the way that applications get sockets. There is not a new "type" of socket for SIP. SIP applications continue use AF_INET in the "domain" argument (the first argument) of the socket() call. For example, to get a TCP socket, a SIP application would call: s = socket (AF_INET, SOCK_STREAM, 0); and to get a UDP socket, a SIP application would call: s = socket (AF_INET, SOCK_DGRAM, 0); Once the application has a socket, it may use either the AF_SIP or the AF_INET form of the sockaddr_in structure in the system calls that pass addresses in to the kernel. The form of sockaddr_in structure returned to the application by the socket functions is determined by the form of addresses passed in most recently. For example, if an application uses an AF_SIP form address in its bind() call, then a subsequent accept() call would return a sockaddr_in structure in the AF_SIP form. If the application calls no socket function that passes in an address before calling one that returns an address, then that function returns an AF_INET form address. The socket functions to which an application passes in an address argument are: bind() connect() sendto() The socket functions that return an address to the application are: accept() recvfrom() getpeername() getsockname() 3.4 Sockets passed across exec() Having two forms of sockaddr_in address that may be used interchageably with all AF_INET sockets means that existing applications are supported straightforwardly, and that new applications simply need to be written to use the AF_SIP form of sockaddr_in. However, Unix allows open sockets to be passed across an exec() call. Thus a newly written application may find itself holding an open socket that was passed to it via exec(), but not knowing what form of addresses that will be returned. Similarly, a newly written SIP knowledgeable application may wish to use AF_SIP form addresses in its bind() or connect() calls, and then pass the socket to a pre-existing application. To remedy these problems, we defined a new setsockopt() option that allows an application to explicitly set the form of sockaddr_in addresses returned by the socket functions. A SIP knowledgeable application that is passed an open socket from an unknown process may do use the SIP_ADDRFORM setsockopt() option to "convert" the socket to return AF_SIP form addresses. Similarly, a SIP knowledgeable application that is about to pass an open socket to a program that may not be SIP knowledgeable may "downgrade" the socket to return AF_INET form addresses using the same setsockopt option. The macro definition for this new option in is: #define SIP_ADDRFORM 0x16 /* get/set form of returned addrs */ The SIP_ADDRFORM option is at the IPPROTO_IP level. The only valid option values are AF_SIP and AF_INET. For example, to convert a socket to return AF_INET form addresses, a program would call: int addrform = AF_SIP; if (setsockopt(s, IPPROTO_IP, SIP_ADDRFORM, (char *) &addrform, sizeof(addrform)) == -1) perror("setsockopt SIP_ADDRFORM"); An application may use SIP_ADDRFORM in the getsockopt() function to learn the form of addresses that will be returned by an open socket. 3.5 IPv4 addresses in AF_SIP form sockaddr_in structures An application may need to pass 32 bit IPv4 address in a sockaddr_in structure, but want to set the sin_family field to AF_SIP in order to set the form of addresses to be returned. To allow for this, we have defined a convention for storing 32 bit IPv4 addresses in 64 bit SIP addresses. The 32-bit IPv4 address is stored in the low-order 32 bits of the SIP address and the high-order 32 bits (i.e. the prefix part) are set to zero. For example, the following structure: struct sockaddr_in sin1; sin1.sin_family = AF_SIP; sin1.sip_zero = 0; *((long *) sin1.sip_addr) = 0; *((long *) &sin1.sip_addr[4]) = inet_addr("192.9.9.1"); And this structure: struct sockaddr_in sin2; sin2.sin_family = AF_INET; sin2.sin_addr.s_addr = inet_addr("192.9.9.1"); bzero(sin2.sin_zero, sizeof(sin_zero); are alternative ways to represent the same IPv4 address. All of the socket functions that accept sockaddr_in addresses passed in as arguments accept IPv4 addresses in the AF_SIP form. 3.6 Flow IDs The SIP header has a 28 bit field reserved to hold a "flow ID". The usage of this field has not yet been specified. But in order to allow application programmers to experiment with SIP flows, we have defined a setsockopt() option that can be used to set the value to be assigned to the flow ID field of transmitted SIP packets. So far, we have not defined any interface for an application to learn the flow ID set in received SIP packets. The macro definition for this new option in is: #define SIP_FLOWID 0x15 /* SIP flow identifier */ The SIP_FLOWID option is at the IPPROTO_IP level. An example of how an application might use this option is: int flowid = 0x0f3c91ab; /* random value made up for this example */ if (setsockopt(s, IPPROTO_IP, SIP_FLOWID, (char *) &flowid, sizeof(flowid)) == -1) perror("setsockopt SIP_ADDRFORM"); The SIP_FLOWID option can also be used with the getsockopt() option to retrieve the flow ID value given in the last setsockopt() call. Since the definition of SIP flows may change as the protocol develops, this option may also change in the future. Thus application developers should consider this option "experimental." 3.7 Name-to-address translation functions We have defined a new function call with the form: struct hostent *hostname2addr(const char *name, int af, char *buf, int buflen); This interface looks the given host name up in the naming service and returns a list of addresses. The name argument is the name of the host to look up. The af argument specifies what form of address -- 4-byte IPv4 or 8-byte SIP address -- to return to the caller in the h_addr_list field of the hostent structure. The buf argument points to a buffer that the function can use to store the hostent structure that is returned. The buflen argument is the size of the buffer. If buf is a null pointer, the function uses its own static buffer. If the af argument is AF_INET, hostname2addr() behaves like gethostbyname(). That is, it returns a hostent structure which includes an array of 4-byte IPv4 addresses. If AA (SIP address) records are found, then the low-order 4 bytes of each SIP address are returned. If only A records are found, then the associated 4-byte IP address is returned. If the type argument is AF_SIP, then the h_addr_list field of the hostent structure contains an array of 8-byte SIP addresses. If AA records are found, then the full SIP address of each record is returned. If only A records are found, then each 8-byte address returned holds the IPv4 address in the low-order 4 bytes and the high-order 4 bytes are set to zero. (i.e. it uses the convention for storing IPv4 addresses in SIP addresses defined above). There was some discussion on the SIP mailing list of some other ways to cast this function. This proposal allows the application to be simple. Since it passes in the address family, the application knows the size of the returned addresses and can unconditionally bcopy() them into sockaddr_in structures. Also, the semantics of the hostname2addr() when af is AF_INET lets us implement the existing gethostbyname() function in terms of the new function. The gethostbyaddr() function remains unchanged: struct hostent *gethostbyaddr(const char *addr, int len, int af); If the af argument is AF_INET, then len must be 4 and addr points to a 4-byte IPv4 address. If af is AF_SIP, then len must be 8 and addr points to an 8-byte SIP address. 3.8 Address input/output functions BSD Unix provides two functions -- inet_addr() and inet_ntoa() -- to convert IPv4 address between binary and printable form. SIP applications need similar functions. We have defined the following two functions to convert SIP and IPv4 addresses: int ascii2addr(long af, char *cp, char *ap); and char *addr2ascii(long af, char *ap, char *buf, int buflen) The first function is used to transform an ascii string into an address data structure. The af argument specifies that address family of the address. Currently AF_INET and AF_SIP are supported. The cp argument points to the ascii string being passed in. The ap argument points to the 4-byte or 8-byte buffer that the address will be converted into. If the af argument is AF_INET, the function accepts a string in the standard IPv4 dotted decimal form: ddd.ddd.ddd.ddd Where ddd is a one to three digit decimal number between 0 and 255. If the af argument is AF_SIP, then the function accepts a string in the standard IPv4 dotted decimal form given above, or a string in one of the two standard SIP printing forms: xxxx:xxxx:ddd.ddd.ddd.ddd or xxxx:xxxx:xxxx:xxxx where xxxx is a 1 to 4 digit hex value and ddd is a 1 to 3 digit decimal number between 0 and 255. If the string is in the IPv4 dotted decimal form, the high order 4 bytes of the address buffer pointed to by ap are set to zero and the low-order 4 bytes are set to the IPv4 address. Having the AF_SIP variant of the ascii2addr() function accept either IPv4 addresses or SIP addresses allows an application to easily accept either form of address from the user. The second function transforms an address buffer into a printable string. The af argument specifies the form of the address. This can be AF_INET or AF_SIP. The ap argument points to a buffer holding a 4-byte IPv4 address or an 8-byte SIP address. The buf argument points to a buffer that the function can convert into, and the buflen argument gives the size of this buffer. If the buf argument is zero, then the function will convert the address unto a private static buffer of its own. In either case, the function returns a pointer to the buffer that the address has been converted into. The ascii2addr() function returns 0 if the conversion succeeded, and -1 if it failed. The function does not modify the storage pointed to by ap if the conversion fails. The addr2ascii() function returns a pointer to the buffer if the conversion succeeded, and -1 if the conversion failed. These two functions are derived from suggestions by Craig Partridge on the SIP mailing list. There was some discussion about whether the buffer that addr2ascii() converts into should be static within the function() or passed in by the application. In the interface presented here, we try to provide the best of both worlds. 4. Open Issues The API changes described here are likely to change as we get experience writing SIP applications. There are also some issues left unresolved. Some of the things we need to think about are: - There is a type defined (in_addr) to represent a 32-bit IPv4 address. Should we define a structure to hold the 64-bit SIP address? There was some discussion of this on the SIP mailing list with opinions on both sides. - The format of the "/etc/hosts" file currently supports only IPv4 addresses. Should we extend the format of the existing file to handle SIP addresses? Or should we invent a file with a new name for storing SIP host addresses? Andrew Cherenson suggests a clever, but ugly, hack to re-use the same file and allow old and new versions of gethosb* to co-exist: precede SIP lines with "#SIP", which acts as comment for old versions. - How do datagram applications participate in path MTU discovery? - What about the SIOC* ioctl() commands that get and set interface parameters and manipulate the IP routing table? - Posix is changing and standardizing the socket interface. Should we track the posix changes? In particular, 4.3-Reno has split the 16-bit address family field of the sockaddr struct into two 8-bit fields: a length field and an address family field. Should we adopt this here? - This spec has concentrated on the BSD socket interface. What about the System V Transport Library Interface (TLI)? Does anyone really care about TLI? - The BSD "net-2" release includes setsockopt options to set the IP TTL. Should we specify that this option sets the SIP hop limit, or is this just obvious? 5. Security Considerations Security issues are not discussed in this document. 6. Acknowledgements Thanks to all who provided feedback to the first revision of this document, including Christian Huitema, Craig Partridge, Steve Deering, Andrew Cherenson, Charles Lynn, Ran Atkinson, Erik Nordmark, Glenn Trewitt, Fred Baker, Robert Elz, and Dean D. Throop. 7. References [1] Deering, S., "Simple Internet Protocol (SIP) Specification", Internet Draft November 1992 8. Authors' Address Robert E. Gilligan Sun Microsystems, Inc. 2550 Garcia Avenue Mailstop UMTV05-44 Mountain View, California 94043-1100 415-336-1012 bob.gilligan@eng.sun.com